import time, re
import pandas as pd
import redis
import pickle
import logging
import pywencai
import akshare as ak
import pandas as pd
import configparser
import datetime
from sqlalchemy import create_engine
import pymysql
import os
import traceback
from 个股行业与概念涨幅分析回测 import get_brand_and_concept_rank

pymysql.install_as_MySQLdb()

log_format = "%(asctime)s - %(levelname)s - %(process)d - %(filename)s:%(lineno)d - %(message)s"
date_format = "%Y-%m-%d %H:%M:%S"  # 精确到秒
logging.basicConfig(level=logging.DEBUG, format=log_format, datefmt=date_format)

pid = os.getpid()
# query_date = datetime.datetime.now().strftime('%Y%m%d')


# 初始化配置解析器
config = configparser.ConfigParser()

# 读取配置文件
import os

current_dir = r"C:\Users\Administrator\Desktop\project\gitee\transaction-backtesting"
config.read(current_dir + '/config.ini', encoding='utf-8')

# 获取Redis的配置信息
redis_host = config.get('Redis', 'host')
redis_port = config.getint('Redis', 'port')
redis_db = config.getint('Redis', 'db')
redis_password = config.get('Redis', 'password')
r = redis.Redis(host=redis_host, port=redis_port, db=redis_db, password=redis_password)

mysql_port = 3306
mysql_host = "127.0.0.1"
mysql_db = "stock"
mysql_password = "123456"
mysql_user = "root"
db_url = f'mysql://{mysql_user}:{mysql_password}@{mysql_host}:{mysql_port}/{mysql_db}'

engine = create_engine(db_url, pool_size=20, max_overflow=20, pool_recycle=60)


# subscriber = r.pubsub()
# subscriber.subscribe('bjzt_channel')

# query_date = datetime.datetime.now().strftime('%Y-%m-%d')

def time_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()  # 记录函数开始执行的时间
        result = func(*args, **kwargs)  # 调用函数
        end_time = time.time()  # 记录函数执行完毕的时间
        print(f"{func.__name__} executed in {end_time - start_time:.4f} seconds")
        return result

    return wrapper


def get_next_trade_date(trade_date):
    tool_trade_date_hist_sina_df = ak.tool_trade_date_hist_sina()
    # 筛选出所有晚于给定交易日的日期
    date_df = tool_trade_date_hist_sina_df[tool_trade_date_hist_sina_df["trade_date"] > pd.Timestamp(trade_date).date()]
    date_df = date_df.sort_values(by="trade_date", ascending=True)  # 按日期升序排序
    next_trade_date = date_df["trade_date"].values[0]  # 获取最接近给定日期的下一个交易日
    return next_trade_date.strftime("%Y%m%d")  # 格式化日期


# 定义获取前一个交易日的函数
def get_pre_trade_date(trade_date):
    tool_trade_date_hist_sina_df = ak.tool_trade_date_hist_sina()
    date_df = tool_trade_date_hist_sina_df[tool_trade_date_hist_sina_df["trade_date"] < pd.Timestamp(trade_date).date()]
    date_df = date_df.sort_values(by="trade_date", ascending=False)
    pre_trade_date = date_df["trade_date"].values[0]
    return pre_trade_date.strftime("%Y%m%d")


def get_pre_trade_date_n(trade_date, n):
    tool_trade_date_hist_sina_df = ak.tool_trade_date_hist_sina()
    date_df = tool_trade_date_hist_sina_df[tool_trade_date_hist_sina_df["trade_date"] < pd.Timestamp(trade_date).date()]
    date_df = date_df.sort_values(by="trade_date", ascending=False)
    pre_trade_date = date_df["trade_date"].values[n - 1]
    return pre_trade_date.strftime("%Y%m%d")


def analysis(stock_name, timestamp, test_info):
    try:
        logging.info(f"开始监控：{stock_name}")

        stock_fupan_df = test_info["df_stock_fupan"]
        stock_info = stock_fupan_df.query("股票简称==@stock_name")



        df_tdx = pd.merge(test_info["df_tdx"], test_info["df_stock_fupan"][["股票代码", "股票简称"]], how="inner",
                          left_on="code", right_on="股票代码")


        df_tdx = df_tdx.query(f"timestamp>='{timestamp}'")

        df_s = df_tdx.query("股票简称==@stock_name and timestamp>=@timestamp").head(200)
        df_s = df_s.sort_values(by="timestamp", ascending=True)

        df_stock_concept = test_info["df_stock_concept"]


        market_value = stock_info['流通市值'].values[0]
        logging.info(f"市值:{market_value}亿")

        lianban = stock_info['连续涨停天数'].values[0]
        logging.info(f"连续涨停天数:{lianban}")
        code = stock_info["股票代码"].values[0]
    except Exception as e:
        tb_info = traceback.format_exc()
        # 将异常信息和 traceback 信息一起记录
        logging.info(f"An error occurred: {e}\nTraceback info:\n{tb_info}")
        return False, "获取股票信息失败"

    is20 = False

    if "ST" in stock_name:
        return False, "ST股票，退出"

    if lianban >= 1:
        return False, "连板股票，退出"

    if code.startswith("30") or code.startswith("688"):
        is20 = True

    time_index = 0

    logging.info("关联股票信息")
    df_stock_relation = test_info["df_stock_relation"]
    df_stock_relation = df_stock_relation.query(f"原股票=='{stock_name}'")

    df_stock_relation_tdx = pd.merge(df_stock_relation,df_tdx,how="inner",left_on="关联股票",right_on="股票简称")

    df_stock_relation_tdx = df_stock_relation_tdx.sort_values('timestamp', ascending=True)

    # 然后根据'stock_name'进行分组，并从每个分组中取出前200条记录
    df_stock_relation_tdx = df_stock_relation_tdx.groupby('关联股票').head(200)


    logging.info(df_stock_relation_tdx)
    df_stock_relation_tdx.to_excel("关联股票涨幅.xlsx",index=False)

    # 遍历df_s
    for index, row in df_s.iterrows():

        real_stock_info_tdx = row

        try:
            timestamp = row["timestamp"]

            real_stock_info_tdx["amount"] = real_stock_info_tdx["amount"] / 10000
            # df = pd.merge(stock_fupan_df,real_stock_info_tdx,how = "inner",left_on=["股票代码"], right_on=["code"])
            # df = df[df["股票简称"] == stock_name]

            # df = pd.merge(df, stock_rps_df, how="inner", left_on=["股票简称"], right_on=["股票简称"])


            logging.info("分析关联股票")
            df_stock_relation_tdx_s = df_stock_relation_tdx.query(f"timestamp=='{timestamp}' and type=='涨幅'")



            logging.info(f"关联股票平均涨幅:{df_stock_relation_tdx_s["change"].mean()}")



            t = {}
            t["code"] = stock_info["股票代码"].values[0]
            t["name"] = stock_info["股票简称"].values[0]
            t["amount"] = round(row["amount"], 2)
            t["change"] = round(row["change"], 2)
            t["price"] = round(row["price"], 2)
            t["vol"] = round(row["vol"], 2)
            t["yesterday_amount"] = round(stock_info["成交额"].values[0], 2)
            t["market_value"] = round(stock_info["流通市值"].values[0], 2)
            t["hsl"] = round(t["amount"] * 100 / t["market_value"], 2)
            t["zhsl"] = round(t["amount"] * 100 / t["yesterday_amount"], 2)
            t["open"] = round(row["open"], 2)
            t["last_close"] = round(row["last_close"], 2)
            t["avr_change"] = round(row["avr_change"], 2)
            t["bias5"] = round(stock_info["5日乖离"].values[0], 2)
            t["bias10"] = round(stock_info["10日乖离"].values[0], 2)
            t["bias20"] = round(stock_info["20日乖离"].values[0], 2)
            t["zt_1_year"] = stock_info["近一年涨停次数"].values[0]
            # stock_info["rps20"] = df["rps20"].values[0]
            t["timestamp"] = row["timestamp"]

            # 买入时的判断参数
            if is20 == False:
                zhsl_low = 60
                zhsl_high = 125
                change_low = 6
                change_high = 9
                avr_change_low = 5
                avr_change_diff = 3
                bais10 = 99
                bais5 = 99
                market_value_min = 15
                market_value_max = 200
                amount = 0.3
                concept_rps = 0
                suggestion = "板块10cm领涨，买"
            else:
                bais10 = 10
                bais5 = 10
                zhsl_low = 60 * 1.5
                zhsl_high = 125 * 1.5
                change_low = 15
                change_high = 18
                avr_change_low = 10
                avr_change_diff = 3
                market_value_min = 15
                market_value_max = 200
                amount = 0.3
                concept_rps = 0
                suggestion = "板块20cm领涨，买"

            """
            买入约束条件：
            1、主板在7-9个点之间，创业板在13-19点之间
            2、自由换手不能太大，太大表明卖的多，是个弱板，涨停时接近昨日成交额较好或者缩量较好。
            3、概念板块所在的涨幅排名不能太低，换算成百分比，在95左右比较好。概念取所有概念中，涨幅第一的概念，作为当日该股的主要炒作概念。
            4、月乖离不能太大，太大容易发生冲高回落。stock_info["bias20"] > 100这个主要是针对没有月线的新股设置的
            5、同一概念中，只做前排个股。对于主板个股，剔除一字板和20cm的个股后，如果涨幅排名还是第一名，则买入。创业板个股，只买涨幅排名第一的个股，其它情况全部放弃。
            6、暂定买入市值大于10亿的股票
            7、暂定买入已成交金额4000万以上的股票
            """

            if t["yesterday_amount"] < 0.5:
                return False, "昨日成交额太小"


            if (t["change"] > change_low
                    and t["change"] < change_high
                    and t["zhsl"] > zhsl_low
                    and t["zhsl"] < zhsl_high
                    and t["amount"] > amount
                    and (t["bias5"] < bais5 or t["bias5"] > 100)
                    # and (stock_info["bias10"] < bais10 or stock_info["bias10"] > 100)
                    # and t["concept_rps_hs"]>=concept_rps
                    and t["market_value"] > market_value_min
                    and t["market_value"] < market_value_max
                    # and stock_info["suggestion"]==suggestion
                    and time_index < 180):

                key = f"stock_buy_{test_info['query_time']}:{t['name']}"
                if r.get(key) is None:
                    t["desc"] = "买入"
                    pd.DataFrame([t]).to_sql("analysis_stock_step_test", engine, if_exists='append', index=False)
                    s = f"{t['name']}满足条件，买入"

                    return True, s

                else:
                    s = f"{t['name']}已经买入"
                    logging.info(s)
                    return False, s

            else:
                unsatisfied_conditions = []
                if not (t["change"] > change_low):
                    unsatisfied_conditions.append("涨幅小于最小值")
                if not (t["change"] < change_high):
                    unsatisfied_conditions.append("涨幅大于最大值")
                if not (t["zhsl"] > zhsl_low):
                    unsatisfied_conditions.append("自由换手太小")
                if not (t["zhsl"] < zhsl_high):
                    unsatisfied_conditions.append("自由换手太大")
                if not (t["amount"] > amount):
                    unsatisfied_conditions.append("交易额太小")
                if not (t["bias5"] < bais5 or t["bias5"] > 100):
                    unsatisfied_conditions.append("5日乖离率太大")
                # if not (stock_info["bias10"] < bais10 or stock_info["bias10"] > 100):
                #    unsatisfied_conditions.append("bias10 < bais10 or bias10 > 100")
                # if not (t["concept_rps_hs"] > concept_rps):
                #     unsatisfied_conditions.append("板块强度太小")
                if not (t["market_value"] > market_value_min):
                    unsatisfied_conditions.append("市值太小")
                if not (t["market_value"] < market_value_max):
                    unsatisfied_conditions.append("市值太大")
                # if not (stock_info["suggestion"] != '不买'):
                #     unsatisfied_conditions.append("板块内非前排")
                # if not (stock_info["rps20"] < 99):
                #     unsatisfied_conditions.append("个股rps值太大")
                # if not (time_index < 200):
                #     unsatisfied_conditions.append("监控时间超过200秒")

                t["desc"] = "; ".join(unsatisfied_conditions)

            logging.info(t)
            pd.DataFrame([t]).to_sql("analysis_stock_step_test", engine, if_exists='append', index=False)

            if t["change"] > 9.8 and is20 == False:
                return False, "已经10cm涨停"

            if t["change"] > 19.8 and is20 == True:
                return False, "已经20cm涨停"

            # if t["rps20"] > 99:
            #     return False,"个股rps值太大"

        except Exception as e:
            tb_info = traceback.format_exc()
            s = f"An error occurred: {e}\nTraceback info:\n{tb_info}"
            logging.info(s)
            return False, s


def get_time_200_seconds_later(start_time_str):
    from datetime import datetime, timedelta
    start_time = datetime.strptime(start_time_str, "%Y-%m-%d %H:%M:%S")
    # 计算开始时间200秒后的时刻
    time_200_seconds_later = start_time + timedelta(seconds=200)
    return time_200_seconds_later.strftime("%Y-%m-%d %H:%M:%S")


@time_decorator
def get_test_info(query_time):
    t = {}
    # 整理时间格式信息
    # end_time = get_time_200_seconds_later(query_time)
    # datetime_obj = datetime.datetime.strptime(query_time, "%Y-%m-%d %H:%M:%S")
    formatted_date = query_time.replace("-", "")
    t["query_time"] = formatted_date

    logging.info("获取股票基本信息")
    df_stock_fupan = pd.read_parquet(f'{current_dir}/data/stock_fupan.parquet')
    df_stock_fupan = df_stock_fupan[df_stock_fupan['日期'] == formatted_date]
    df_stock_fupan.fillna(0, inplace=True)
    t["df_stock_fupan"] = df_stock_fupan

    logging.info("获取短线精灵数据")
    df_dxjl = pd.read_parquet(f'{current_dir}/data/dxjl/dxjl_{formatted_date}.parquet')
    t["df_dxjl"] = df_dxjl

    logging.info("获取交易数据")
    df_tdx = pd.read_parquet(f'{current_dir}/data/tdx/new_tdx_{formatted_date}.parquet')
    t["df_tdx"] = df_tdx

    logging.info("获取股票关联信息")
    df_stock_relation = pd.read_parquet(f'{current_dir}/data/correlations/correlations_{formatted_date}.parquet')
    t["df_stock_relation"] = df_stock_relation

    logging.info("获取股票概念涨幅信息")
    df_stock_concept = pd.read_parquet(f'{current_dir}/data/concept/concept_{formatted_date}.parquet')
    t["df_stock_concept"] = df_stock_concept

    return t



if __name__ == "__main__":
    logging.info("监控进程启动")

    query_day = "2024-05-30"

    res = get_test_info(query_day)

    df_dxjl = res["df_dxjl"]
    df_stock_fupan = res["df_stock_fupan"]
    df_tdx = res["df_tdx"]

    df_dxjl = pd.merge(df_dxjl, df_stock_fupan[["股票简称", "连续涨停天数","成交额"]], how="inner", left_on="stock_name",
                       right_on="股票简称")

    df_dxjl = df_dxjl.query("连续涨停天数==0")
    df_dxjl = df_dxjl.query("成交额>0.5")
    # 遍历df_dxjl，获取每个时间点的股票信息
    l = []

    for index, row in df_dxjl.iterrows():
        analysis(row["stock_name"], row["timestamp"], res)
    # print(analysis("扬帆新材", f"{query_day} 13:10:15", res))

    # df = pd.concat(l)
    # pd.merge(df,df_stock_fupan[["股票代码","股票名称",'连续涨停天数']],how="inner",left_on="股票代码",right_on="股票代码")
    # df.to_excel("result.xlsx",index=False)








